import SimpleMDE from "react-simplemde-editor";
import "easymde/dist/easymde.min.css";
import { useMemo, useState } from "react";
import { v4 as uuidv4 } from "uuid";
import { toast, ToastContainer } from "react-toastify";
import "react-toastify/dist/ReactToastify.css";

import "./App.css";
import FileSearch from "./components/FileSearch";
import FileList from "./components/FileLIst";
import BottomBtn from "./components/BottomBtn";
import TabList from "./components/TabList";
import Loader from "./components/Loader";
// import defaultFiles from "./utils/defaultFiles";
import { flattenArr, objToArr, timestampToString } from "./utils/helper";
import fileHelper from "./utils/fileHelper";
import useIpcRenderer from "./hooks/useIpcRenderer";

const { ipcRenderer } = window.require("electron");
const { join, basename, extname, dirname } = window.require("path");
// browser 环境，从@electron/remote引入，require前面必须加window
const { app, dialog } = window.require("@electron/remote");
// 用于接收 main.js 发送的事件
// const { ipcRenderer } = window.require("electron");
const Store = window.require("electron-store");
const fileStore = new Store({ name: "Files Data" });
// console.log(fileStore);
const settingsStore = new Store({ name: "Settings" });
// fileStore.delete("files");
// settingsStore.delete("savedFileLocation");

// 获取是否允许自动同步状态
// 必须在"accessKey", "secretKey", "bucketName" 三个值都有填写，并且勾选了自动同步checkbox时，才能自动同步
const getAutoSync = () => {
    return ["accessKey", "secretKey", "bucketName", "enableAutoSync"].every((key) => !!settingsStore.get(key));
};

// 传进来的是 扁平对象 --》 {id:{}}
const saveFilesToStore = (files) => {
    // 精简数据，每个对象存储到store只有id，title，path，createdAt
    const filesStoreObj = objToArr(files).reduce((result, cur) => {
        const { id, path, title, createdAt, isSynced, updatedAt } = cur;
        result[id] = { id, path, title, createdAt, isSynced, updatedAt };
        return result;
    }, {});
    fileStore.set("files", filesStoreObj);
};

function App() {
    // 获取本地某一个目录
    const savedLocation = settingsStore.get("savedFileLocation") || app.getPath("documents");

    // 全部文件
    const [files, setFiles] = useState(fileStore.get("files", {}) || {});
    // 当前打开的文件id
    const [activeFileID, setActiveFileID] = useState("");
    // 所有打开的文件id --> 右侧tabList列表
    const [openedFileIDs, setOpenedFileIDs] = useState([]);
    // 未保存的文件id
    const [unsavedFileIDs, setUnsavedFileIDs] = useState([]);
    // 原始files。[{},{}]
    const filesArr = objToArr(files);
    // 搜索
    const [searchedFiles, setSearchedFiles] = useState([]);
    // loading
    const [isLoading, setLoading] = useState(false);

    // 所有打开的文件
    const openedFiles = openedFileIDs.map((openID) => {
        return files[openID];
    });
    // 当前打开的文件
    const activeFile = files[activeFileID];

    // 左侧搜索出的数组，如果搜索的数组有值，就用搜索的数组，没有就用files
    const fileListArr = searchedFiles.length ? searchedFiles : filesArr;

    /*左侧列表方法*/
    // 点击列表中的文件
    const fileClick = (fileID) => {
        // 设置当前激活的文件
        setActiveFileID(fileID);

        const currentFile = files[fileID];
        const { id, title, path, isLoaded } = currentFile;
        if (!isLoaded) {
            // 已经开启自动同步
            if (getAutoSync()) {
                // 向main.js发送一个下载事件
                ipcRenderer.send("download-file", {
                    key: `${title}.md`,
                    path,
                    id,
                });
            } else {
                // 未开启自动同步
                fileHelper.readFile(currentFile.path).then((value) => {
                    const newFile = { ...files[fileID], body: value, isLoaded: true };
                    setFiles({ ...files, [fileID]: newFile });
                    // 这里只需要更新界面，所以不需要重新存储到store
                });
            }
        }

        // 把点击的文件添加到openedFileIDs -->右侧tabList列表
        // 如果不存在再添加进去
        if (!openedFileIDs.includes(fileID)) {
            // 先把旧的openedFileIDs展开再合并新的
            setOpenedFileIDs([...openedFileIDs, fileID]);
        }
    };
    // 删除文件方法
    const deleteFile = (id) => {
        // const newFile = files.filter((file) => file.id !== id);
        // setFiles(newFile);

        // 课程有这段代码，课程里。编辑时按esc会报错，自己代码不会，所以先注释
        // if (files[id].isNew) {
        //     const {[id]:value,...afterDelete} = files
        //     setFiles(afterDelete);
        //     return;
        // }

        fileHelper
            .deleteFile(files[id].path)
            .then(() => {
                const { [id]: value, ...afterDelete } = files;
                setFiles(afterDelete);
                saveFilesToStore(afterDelete);
                toast.success("删除成功!");
                // 关闭打开的tab
                tabClose(id);
            })
            .catch(() => {
                toast.error("文件不存在，删除失败!");
                const filterFiles = filesArr.filter((file) => file.id !== id);
                setFiles(flattenArr(filterFiles));
                saveFilesToStore(flattenArr(filterFiles));
            });
    };
    // 编辑文件名称方法
    const updateFileName = (id, title, isNew) => {
        // console.log(isNew);
        // console.log(join(savedLocation, `${title}.md`));
        // console.log(join(dirname(files[id].path), `${title}.md`));
        // 如果是新文件，就改创建新文件目录下的文件名。如果是旧文件或者是在其他地方导入的文件，就改对应目录下的文件名
        const newPath = isNew ? join(savedLocation, `${title}.md`) : join(dirname(files[id].path), `${title}.md`);
        const modifiedFile = { ...files[id], title: title, isNew: false, path: newPath };
        const newFiles = { ...files, [id]: modifiedFile };

        // 判断文件是否存在
        for (const item of filesArr) {
            if (item.title === title) {
                toast.error("文件名已存在!");
                const filterFiles = filesArr.filter((file) => file.id !== id);
                setFiles(flattenArr(filterFiles));
                return;
            }
        }
        // const existFile = filesArr.find((file) => file.path === newPath && file.id !== id);
        // console.log("existFile", existFile);
        // if (existFile) {
        //     toast.error("文件名已存在!");
        //     const filterFiles = filesArr.filter((file) => file.id !== id);
        //     setFiles(filterFiles);
        //     return;
        // }

        // 新创建的文件
        if (isNew) {
            // 拼接路径并写入文件
            fileHelper.writeFile(newPath, files[id].body).then(() => {
                console.log("写入成功");
                setFiles(newFiles);
                saveFilesToStore(newFiles);
                toast.success("新建文件成功!");
            });
        } else {
            // 修改文件名
            const oldPath = files[id].path;
            fileHelper.renameFile(oldPath, newPath).then(() => {
                console.log("重命名成功");
                setFiles(newFiles);
                saveFilesToStore(newFiles);
                toast.success("重命名成功!");
            });
        }
    };
    // 创建文件方法
    const createNewFile = () => {
        const newID = uuidv4();
        const newFile = {
            id: newID,
            title: "",
            body: "## 请输入markdown",
            createdAt: new Date().getTime(),
            isNew: true,
        };
        setFiles({ ...files, [newID]: newFile });
    };
    // 文件搜索
    const fileSearch = (keyword) => {
        // 搜索所有文件，过滤title出包含搜索文字的文件
        const newFiles = filesArr.filter((file) => file.title.includes(keyword));
        setSearchedFiles(newFiles);
    };
    //导入文件方法
    const importFiles = () => {
        dialog
            .showOpenDialog({
                title: "选择导入的Markdown文件",
                properties: ["openFile", "multiSelections"],
                filters: [
                    {
                        name: "Markdown files",
                        extensions: ["md"],
                    },
                ],
            })
            .then(({ filePaths }) => {
                if (Array.isArray(filePaths)) {
                    // 过滤数组，不添加已经有的文件
                    const filteredPaths = filePaths.filter((path) => {
                        // 判断files是否存在文件
                        // 用 Object.values(files) 去值，返回值的数组，再用find找是否有重复的路径，返回对应的文件
                        const alreadyAdded = Object.values(files).find((file) => {
                            return file.path === path;
                        });
                        if (alreadyAdded) {
                            toast.error("文件已存在!");
                        }
                        // 如果文件重复就会返回false，不存在就返回true
                        return !alreadyAdded;
                    });
                    // 扩展数组，添加对应的id，title，path
                    const importFileArr = filteredPaths.map((path) => {
                        return {
                            id: uuidv4(),
                            // extname 取文件后缀
                            // basename 第二个参数是去除文件后缀，只留文件名
                            // 这里只能添加md文件，basename第二给参数直接写md也是可以的
                            title: basename(path, extname(path)),
                            path,
                        };
                    });
                    // 获取flattenArr {id:{},id:{}}
                    const newFiles = { ...files, ...flattenArr(importFileArr) };
                    // 存储到electron store
                    setFiles(newFiles);
                    saveFilesToStore(newFiles);
                    // 弹窗导入成功
                    if (importFileArr.length > 0) {
                        toast.success("导入成功!");
                    }
                }
            });
    };

    /*右侧tabs方法*/
    // tab切换时设置当前打开的文件
    const tabClick = (fileID) => {
        // console.log("当前打开文件的id -- " + fileID);
        setActiveFileID(fileID);
    };
    // tab关闭某一个文件
    const tabClose = (id) => {
        // 使用过滤器取除了fileID以=以外的
        const tabsWithout = openedFileIDs.filter((fileID) => {
            return fileID !== id;
        });
        setOpenedFileIDs(tabsWithout);

        // 关闭tab后，如果还有其他的tab，就把tab高亮切换成第一个tab。没有其他的tab，就清空
        if (tabsWithout.length > 0) {
            setActiveFileID(tabsWithout[0]);
        } else {
            setActiveFileID("");
        }
    };
    /*右侧markdown编辑器方法*/
    // 编辑器文本变化时
    const fileChange = (id, value) => {
        // console.log(id, value);
        // 循环所有文件，找出当前正在编辑的文件，把文件的body替换成编辑后的
        // const newFile = files.map((file) => {
        //     if (file.id === id) {
        //         file.body = value;
        //     }
        //     return file;
        // });

        // 当文件有变化时，再执行保存方法
        if (value !== files[id].body) {
            // 铺开某个文件对象，把body替换
            const newFile = { ...files[id], body: value };
            // 铺开所有对象，把修改后的替换
            setFiles({ ...files, [id]: newFile });

            // 修改完，将文件添加未保存文件数组中
            // 判断修改后的文件是否在未保存的数组中
            if (!unsavedFileIDs.includes(id)) {
                setUnsavedFileIDs([...unsavedFileIDs, id]);
            }
        }
    };
    // 保存markdown编辑器的文本，
    const saveCurrentFile = () => {
        const { path, body, title } = activeFile;
        // 写入对应的文件
        fileHelper.writeFile(path, body).then(() => {
            // 写入成功，从未保存id数组删除当前的id
            setUnsavedFileIDs(unsavedFileIDs.filter((id) => id !== activeFile.id));
            // 七牛云三个值都填写并且开启自动同步时
            if (getAutoSync()) {
                ipcRenderer.send("upload-file", {
                    key: `${title}.md`,
                    path,
                });
            }
        });
    };

    // 上传文件成功后的处理函数
    const activeFileUploaded = () => {
        const { id } = activeFile;
        // 给上传的文件加上是否同步，同步时间字段
        const modifiedFile = { ...files[id], isSynced: true, updatedAt: new Date().getTime() };
        const newFiles = { ...files, [id]: modifiedFile };
        setFiles(newFiles);
        saveFilesToStore(newFiles);
    };

    // 下载文件
    const activeFileDownloaded = (event, message) => {
        const currentFile = files[message.id];
        const { id, path } = currentFile;
        fileHelper.readFile(path).then((value) => {
            let newFile;
            if (message.status === "download-success") {
                newFile = {
                    ...files[id],
                    body: value,
                    isLoaded: true,
                    isSynced: true,
                    updatedAt: new Date().getTime(),
                };
            } else {
                newFile = {
                    ...files[id],
                    body: value,
                    isLoaded: true,
                };
            }
            const newFiles = { ...files, [id]: newFile };
            setFiles(newFiles);
            saveFilesToStore(newFiles);
        });
    };

    // 全部文件上传方法
    const filesUploaded = () => {
        console.log("filesUploaded");
        // 转成普通数组
        const newFiles = objToArr(files).reduce((result, file) => {
            // 获取当前时间
            const currentTime = new Date().getTime();
            result[file.id] = {
                ...files[file.id],
                isSynced: true,
                updatedAt: currentTime,
            };
            return result;
        }, {});
        setFiles(newFiles);
        saveFilesToStore(newFiles);
    };

    useIpcRenderer({
        "create-new-file": createNewFile,
        "import-file": importFiles,
        "save-edit-file": saveCurrentFile,
        "active-file-uploaded": activeFileUploaded,
        "file-downloaded": activeFileDownloaded,
        "files-uploaded": filesUploaded,
        "loading-status": (message, status) => {
            setLoading(status);
        },
    });

    // react-simplemde-editor 5.0版本以上得加这些配置，否则编辑器只能输入一次，然后会失焦
    const autofocusNoSpellcheckerOptions = useMemo(() => {
        return {
            autofocus: true,
            spellChecker: false,
            minHeight: "550px",
        };
    }, []);

    return (
        <div className="App container-fluid">
            {isLoading && <Loader />}
            <ToastContainer
                position="top-center"
                autoClose={500}
                hideProgressBar
                newestOnTop={false}
                closeOnClick
                rtl={false}
                pauseOnFocusLoss={false}
                draggable={false}
                pauseOnHover={false}
            />
            <div className="row">
                <div className="col-3 ps-0 left-panel">
                    <FileSearch title="我的云文档" onFileSearch={fileSearch} />
                    <FileList
                        files={fileListArr}
                        onFileClick={fileClick}
                        onSaveEdit={updateFileName}
                        onFileDelete={deleteFile}
                    />
                    <div className="row button-group">
                        <div className="col p-0">
                            <BottomBtn
                                text={"新建"}
                                colorClass={"btn-primary"}
                                icon={"bi-plus-lg"}
                                onBtnClick={createNewFile}
                            />
                        </div>
                        <div className="col p-0">
                            <BottomBtn
                                text={"导入"}
                                colorClass={"btn-success"}
                                icon={"bi-box-arrow-in-down"}
                                onBtnClick={importFiles}
                            />
                        </div>
                    </div>
                </div>
                <div className="col-9 p-0 right-panel">
                    {/* 存在打开的文件 */}
                    {!activeFile && <div className="start-page">选择或者创建新的 MarkDown 文件</div>}

                    {/* 不存在打开的文件 */}
                    {activeFile && (
                        <>
                            <TabList
                                files={openedFiles}
                                activeId={activeFileID}
                                unSaveIds={unsavedFileIDs}
                                onTabClick={tabClick}
                                onCloseTab={tabClose}
                            ></TabList>
                            <SimpleMDE
                                key={activeFile && activeFile.id}
                                value={activeFile && activeFile.body}
                                onChange={(value) => {
                                    fileChange(activeFile.id, value);
                                }}
                                options={autofocusNoSpellcheckerOptions}
                            />
                            {activeFile.isSynced && (
                                <span className="sync-status">
                                    已同步，上次同步{timestampToString(activeFile.updatedAt)}
                                </span>
                            )}
                        </>
                    )}
                </div>
            </div>
        </div>
    );
}

export default App;
